package com.beiding.objectanalyzer;

import lombok.Getter;
import lombok.Setter;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Set;


/*

    描述数组:

        A {
            B[] bs;
        }

      a,..

      描述bs的键:    id(a).bs


    特殊类型的处理  ???

    Collection

    Map



/*

    TODO  排列组合问题

        a:
            bs = new B[10000];

        b:
            as= new A[10000]


        那么
            构建的分析键的数量:
               是 10000*10000

        数量会非常庞大


        一个对象可能被多个对象所持有


  */


@Getter
@Setter
public class KeyNode {


    //类型
    private Class type;

    //所在数组索引
    private int index = -1;

    //是否是与类型相关的,例如:Field,Method,Class等

    //持有者键
    private KeyNode holderNode;

    //字段
    private Field field;

    //持有者对象
    private Object holder;

    //持有者,避免对象重复创建,对象集中所有对象唯一
    private Set<KeyNode> holderNodes;

    private boolean isRoot;

    //值
    private Object value;

    //层级
    private int layer;

    public Class getComponentType() {
        return type.getComponentType();
    }

    //循环的回退步长,在循环的清空下,回退给定的次数可以找到该节点

    private static final Package reflectPackage = Package.getPackage("java.lang.reflect");

    public boolean isStatic() {
        return Modifier.isStatic(field.getModifiers());
    }

    public boolean isObject() {
        if (isField()) {
            return Object.class.isAssignableFrom(field.getType());
        } else if (isComponent()) {
            return Object.class.isAssignableFrom(type);
        }
        return Object.class.isAssignableFrom(type);
    }

    public boolean isTypeAbout() {
        return Class.class.isAssignableFrom(type) || reflectPackage.equals(type.getPackage());
    }

    private KeyNode loopNode;

    private int loopBack = 2;

    public boolean isLoop() {
        if (loopBack == 2) {

            if (holderNode == null) {
                loopBack = 1;
                return false;
            }

            loopNode = holderNode.findLoop(value);

            if (loopNode == null) {
                loopBack = 1;
                return false;
            } else {
                loopBack = loopNode.getLayer() - this.layer;
            }

            return loopBack != 1;
        } else return loopBack != 1;
    }

    private KeyNode findLoop(Object v) {
        if (this.value == v) {
            return this;
        } else {
            if (holderNode == null) {
                return null;
            } else {
                return holderNode.findLoop(v);
            }
        }
    }

    public boolean isArray() {
        return type.isArray();
    }

    public boolean isField() {
        return field != null;
    }

    public boolean isComponent() {
        return index != -1;
    }

    public boolean componentIsObject() {
        return Object.class.isAssignableFrom(getComponentType());
    }


    @Override
    public String toString() {

        String t = null;

        if (isComponent()) {
            t = "[" + index;
        } else if (isField()) {
            t = field.getName();
        }


        if (isRoot()) {
            return "ROOT(" + ObjectUtils.idForObject(value) + ")";
        } else if (isLoop()) {
            return "LOOP(" + loopBack + ",'" + t + "')";
        } else {
            return t;
        }
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof KeyNode)) {
            return false;
        }

        for (Field declaredField : KeyNode.class.getDeclaredFields()) {
            declaredField.setAccessible(true);
            try {

                Object tv = declaredField.get(this);
                Object ov = declaredField.get(obj);

                if (tv != null) {
                    if (!tv.equals(ov)) {
                        return false;
                    }
                } else {
                    if (ov != null) {
                        return false;
                    }
                }

            } catch (Exception ignore) {


            }
        }

        return true;
    }

    @Override
    public int hashCode() {

        int hash = 0;

        for (Field declaredField : KeyNode.class.getDeclaredFields()) {
            declaredField.setAccessible(true);

            try {
                Object o = declaredField.get(this);
                if (o != null) {
                    hash += o.hashCode();
                }
            } catch (IllegalAccessException e) {

            }
        }

        return hash;
    }
}
